#include <QUrl>
#include <QBuffer>
#include <QXmlStreamWriter>
#include <QXmlAttributes>
#include "DataModel.h"
#include "XmlHelper.h"
#include "RequestDescriptor.h"
#include "scImpl.h"

#define FCT "RequestDescriptor::getTestUrl"
QString RequestDescriptor::getTestUrl()
{
	return QString("/SybilServer/RequestHub");
}

#undef FCT
#define FCT "RequestDescriptor::RequestDescriptor()"
RequestDescriptor::RequestDescriptor() :
	transId(0), update(true)
{
}

#undef FCT
#define FCT "RequestDescriptor::RequestDescriptor(R)"
RequestDescriptor::RequestDescriptor(const Request & request) :
	transId(0), update(true)
{
	include(request);
}

#undef FCT
#define FCT "RequestDescriptor::~RequestDescriptor"
RequestDescriptor::~RequestDescriptor()
{
	//for(Requests::iterator it(requests.begin()); it!=requests.end(); ++it)
	//	delete *it;
}

#undef FCT
#define FCT "RequestDescriptor::eraseInc"
void RequestDescriptor::eraseInc(RequestDescriptor::iterator & it)
{
	RequestDescriptor::iterator tmpIt(it);
	++it;
	erase(tmpIt);
}

#undef FCT
#define FCT "RequestDescriptor::toString"
std::string RequestDescriptor::toString() const
{
	ostringstream os;
	os<<"RequestDescriptor ";
	for(RequestDescriptor::const_iterator it(begin()); it!=end(); ++it)
	{
		const Request & rq(it->second);
		os <<rq.entity<<"/"<<rq.stype;
		if(rq.stype==SCOPE_IK || rq.stype==SCOPE_IKTS || rq.stype==SCOPE_PK)
		{
			os<<"(";
			for(unsigned int i(0); i<rq.pks.size(); ++i)
				os <<rq.pks.at(i)<<",";
			os<<")";
		}
		os<<"//";
	}
	return os.str();
}

#undef FCT
#define FCT "RequestDescriptor::include"
void RequestDescriptor::include(const RequestDescriptor & additionalRequ)
{
	for(RequestDescriptor::const_iterator it(additionalRequ.begin()); it!=additionalRequ.end(); ++it)
		include(it->second);
}

#undef FCT
#define FCT "RequestDescriptor::include(R)"
void RequestDescriptor::include(const Request & newRequ)
{
	bool merged(false);
	for(RequestDescriptor::iterator it(begin()); it!=end(); ++it)
	{
		Request & rq(it->second);
		if(rq.rtype!=newRequ.rtype)
			continue; // can't merge for different request types
		if(rq.entity!=newRequ.entity)
			continue; // can't merge for different entities
		if(rq.stype==SCOPE_TAKEALL && (rq.followFk==newRequ.followFk || newRequ.stype==SCOPE_TAKEALL))
		{ // FSCOPE_TAKEALL takes all...
			rq.followFk|=newRequ.followFk;
			merged=true;
			break;
		}
		if(rq.stype!=newRequ.stype)
			continue; // can't merge different scope types
		if(newRequ.stype==SCOPE_TS && rq.deadline==newRequ.deadline)
		{ // they are identical except for followFk
			rq.followFk|=newRequ.followFk;
			merged=true;
			break;
		}
		if(!rq.followFk && newRequ.followFk)
			continue; // can't merge following into non-following (and I'm too lazy to check if we could update followFk...)
		if( (rq.stype==SCOPE_PK && newRequ.stype==SCOPE_PK) ||
				(rq.stype==SCOPE_IK && newRequ.stype==SCOPE_IK) ||
				(rq.stype==SCOPE_IKTS && newRequ.stype==SCOPE_IKTS && rq.deadline==newRequ.deadline) )
		{ // add the key
			for(unsigned int i(0); i<newRequ.pks.size(); ++i)
			{
				if(std::find(rq.pks.begin(), rq.pks.end(), newRequ.pks.at(i))==rq.pks.end())
					rq.pks.push_back(newRequ.pks.at(i));
			}
			merged=true;
			break;
		}
	}
	if(!merged)
		insert(RequestDescriptor::value_type(newRequ.entity, newRequ));
}

#undef FCT
#define FCT "RequestDescriptor::getUrl"
QString RequestDescriptor::getUrl(bool encodeParam) const
{
	if(encodeParam)
	{
		throw new AppException(FCT ": encoding to URL-parameters not supported", true);
		/*
		QUrl url;
		QList<QPair<QString, QString> > query;
		url.setPath(QString("/SybilServer/RequestHub"));
		url.setQueryItems(query);
		return QString(url.toEncoded(QUrl::RemoveScheme | QUrl::RemoveAuthority));
		*/
	}
	else
		return QString("/SybilServer/RequestHub");
}

#undef FCT
#define FCT "RequestDescriptor::createParamsBuffer"
QBuffer * RequestDescriptor::createParamsBuffer() const
{
	QBuffer * res(new QBuffer);
	res->open(QIODevice::WriteOnly);
	QXmlStreamWriter * wr(XmlHelper::createXmlDoc(res));
	toXml(wr);
	XmlHelper::finishXmlDoc(wr);
	res->close();
	return res;
}

#undef FCT
#define FCT "RequestDescriptor::toXml"
void RequestDescriptor::toXml(QXmlStreamWriter * writer) const
{
	DataDomain dm(DOMAIN_UNDEF);
	DataModel *dmod(DataModel::get());
	for(RequestDescriptor::const_iterator it(begin()); it!=end(); ++it)
	{
		if(dm==DOMAIN_UNDEF || dm!=dmod->entityId2Domain(it->first))
		{
			if(dm!=DOMAIN_UNDEF)
				writer->writeEndElement(); // domain
			dm=dmod->entityId2Domain(it->first);
			writer->writeStartElement(dmod->entityId2DomainText(it->first));
		}
		(*it).second.toXml(writer);
	}
	if(dm!=DOMAIN_UNDEF)
		writer->writeEndElement(); // domain
}

ostream& std::operator<< (ostream& out, const sybilpm::RequestDescriptor & requDesc)
{
	out <<requDesc.toString().c_str();
	return out;
}
